#pragma once

#include <vector>
#include <stdint.h>
#include <algorithm>
#include <string>
#include <cassert>
#include <cstring>

#include "Logger.hpp"

using namespace std;
using namespace LogMudule;

// ⽤于实现通信中每一个用户态的连接的接收缓冲区和发送缓冲区
class Buffer
{
    const static uint32_t DefaultBufSize = 4096;

private:
    vector<char> _buf;   // 缓冲区
    uint64_t _read_pos;  // 读指针：下一次从read下标位置开始读取
    uint64_t _write_pos; // 写指针：下一次从write下标位置开始写入（所以write指向的是可读数据一个字节的下一个位置）
public:
    Buffer()
        : _read_pos(0),
          _write_pos(0),
          _buf(DefaultBufSize)
    {
    }
    // 获取write指针指向下标位置的地址
    char *WritePos()
    {
        return &(_buf[0]) + _write_pos;
    }
    // 获取read指针指向下标位置的地址
    char *ReadPos()
    {
        return &(_buf[0]) + _read_pos;
    }
    // 获取write后面的剩余空间大小
    uint64_t TailIdleSize()
    {
        return _buf.size() - _write_pos;
    }
    // 获取read前面的可以被覆盖的无效数据的大小
    uint64_t HeadIdleSize()
    {
        return _read_pos;
    }
    // 将write指针移动n个字节
    void MoveWriteOffset(ssize_t n)
    {
        assert(_write_pos + n >= _read_pos);
        assert(_write_pos + n < _buf.size());

        _write_pos += n;
    }
    // 将read指针移动n个字节
    void MoveReadOffset(ssize_t n)
    {
        assert(_read_pos + n >= 0);
        assert(_read_pos + n <= _write_pos);

        _read_pos += n;
    }
    
    // 确保数组能再装下len字节
    void EnsureWriteSpace(uint64_t len)
    {
        uint64_t size = _buf.size();
        uint64_t read_pos = _read_pos;

        // 写指针后面的空间足够
        if (TailIdleSize() >= len)
        {
            return;
        }
        // 总的剩余空间足够
        else if (TailIdleSize() + HeadIdleSize() >= len)
        {
            // 移动有效数据（_write_pos-_read_pos）到缓冲区开头开头
            std::rotate(_buf.begin(), _buf.begin() + _read_pos, _buf.begin() + _write_pos);

            // 更新读写指针
            // 必须先移动读指针，不然如果有效数据长度<读指针前的数据时，会触发assert
            // 向前移动read_pos个字节
            MoveReadOffset(-read_pos);

            // 向前移动read_pos个字节
            MoveWriteOffset(-read_pos);
        }
        // 总的剩余空间 不够
        else
        {
            // 扩容
            _buf.resize(size + len);
            LOG(LogLevel::DEBUG) << "Bufer扩容" << size + len << "个字节";
        }
    }
    // 获取当前缓冲区中可读的数据的大小
    uint64_t ReadAbleSize()
    {
        return _write_pos - _read_pos;
    }
    // 从缓冲区读取数据,返回成功读取的字节数
    uint64_t Read(void *buf, uint64_t size)
    {
        if(size == 0)
        {
            //size==0就不需要浪费时间调用函数
            return 0;
        }
        // 最多只能读取所有的有效数据
        uint64_t readablesize = ::min(size, ReadAbleSize());

        // 读取数据到buf
        memcpy(buf, (void *)ReadPos(), readablesize);

        // 向后移动read指针
        MoveReadOffset(readablesize);

        return readablesize;
    }
    // 从缓冲区读取数据到指定string中,返回成功读取的字节数
    uint64_t ReadAsString(string *dest, uint64_t size)
    {
        //如果size为0，此时dest可能还没有开辟堆区空间
        //所以dest->at(0)可能野指针访问
        if(size==0)
        {
            //而且size==0本身就不需要浪费时间调用函数
            return 0;
        }
        // 保证dest装得下
        // 但是可能缓冲区中数据不足用户指定的大小size,所以调整到缓冲区的最大大小即可
        if (size > ReadAbleSize())
        {
            dest->resize(ReadAbleSize());
        }
        else//如果缓冲区至少有size个字节的数据
        {
            dest->resize(size);
        }
        // 读取
        uint64_t read_size = Read((void *)(dest->c_str()), size);
        if (read_size == 0)
        {
            LOG(LogLevel::DEBUG) << "ReadAsString：未能读取到数据";
        }
        else if (read_size < size)
        {
            LOG(LogLevel::DEBUG) << "ReadAsString：读取到的数据大小，小于用户指定大小";
        }
        return read_size;
    }
    // 向缓冲区写入数据
    void Write(const void *data, uint64_t size)
    {
        if(size==0)
        {
            //size==0就不需要浪费时间调用函数了
            return;
        }
        // 确保缓冲区能再装下size字节
        EnsureWriteSpace(size);

        // 写入数据
        memcpy((void *)WritePos(), data, size);

        // 移动write指针
        MoveWriteOffset(size);
    }
    // 一次写入一个string类型的数据
    void WriteString(const string &data)
    {
        //如果size为0，此时dest可能还没有开辟堆区空间
        //所以dest[0]可能野指针访问
        if(data.size()==0)
        {
            //而且size==0本身就不需要浪费时间调用函数
            return;
        }
        Write((const void *)data.c_str(), data.size());
    }
    // 一次写入一个Buffer类型的数据
    void WriteBuffer(Buffer &data)
    {
        if(data.ReadAbleSize()==0)
        {
            //size==0就不需要浪费时间调用函数
            return;
        }
        // 把Buffer对象缓冲区中的有效数据写入
        Write((const void *)data.ReadPos(), data.ReadAbleSize());
    }
    // 查找缓冲区中回车字符的位置，并返回其地址
    char *FindCRLF()
    {
        // 从缓冲区的可读数据范围中查找\n
        auto it = std::find(_buf.begin() + _read_pos, _buf.begin() + _write_pos, '\n');
        // 如果没有找到
        if (it == _buf.begin() + _write_pos)
        {
            LOG(LogLevel::DEBUG) << "FindCRLF：未能找到换行符";
            return nullptr;
        }
        return (char *)&(*it);
    }
    // 从缓冲区一次读取一行数据，因为二进制数据不存在读取一行的说法，所以直接返回string
    string GetLine()
    {
        // 查找缓冲区中下一个回车字符的位置，并返回其地址
        char *pos = FindCRLF();
        // 如果没找到
        if (pos == nullptr)
        {
            return "";
        }
        // 计算要读取的大小, 注意\n也会被读取出去,所以需要+1
        int size = pos - ReadPos() + 1;

        string line;
        ReadAsString(&line, size);

        return line;
    }
    // 清空缓冲区
    void Clear()
    {
        // 把read指针和write指针置0即可
        _read_pos = 0;
        _write_pos = 0;
    }
};